package com.smartboard.network.game;

import com.smartboard.network.socket.OnReceiveListener;

import java.io.Serializable;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

/**
 * 游戏server和client之间的socket通信协议
 * Created by cheas on 2018/7/20.
 */

public abstract class AbstractGame implements OnReceiveListener {
    private static final String TAG = "AbstractGame";

    public static final int PORT_GO             = 18062;
    public static final int PORT_CHINESE_CHESS  = 18064;
    public static final int PORT_INTER_CHESS    = 18066;
    public static final int PORT_GOBANG         = 18068;

    //命令列表
    public static final int CMD_LOGIN           = 1;    // 登入
    public static final int CMD_LOGOUT          = 2;    // 登出
    public static final int CMD_CREATE_ROOM     = 3;    // 创建房间
    public static final int CMD_ENTER_ROOM      = 4;    // 进入房间
    public static final int CMD_EXIT_ROOM       = 5;    // 退出房间
    public static final int CMD_GET_ROOMS       = 6;    // 获取所有房间id
    public static final int CMD_CREATE_GAME     = 7;    // 创建对局
    public static final int CMD_JOIN_GAME       = 8;    // 加入对局
    public static final int CMD_QUIT_GAME       = 9;    // 退出对局
    public static final int CMD_PLAY_MOVE       = 10;   // 执行着法（落子）
    public static final int CMD_UNDO            = 11;   // 回退着法（落子）
    public static final int CMD_RESIGN          = 12;   // 投降
    public static final int CMD_DRAW            = 13;   // 求和
    public static final int CMD_GET_MOVES       = 14;   // 获取对局着法列表
    public static final int CMD_READY           = 15;   // 准备好
    public static final int CMD_GAME_START      = 16;   // 对局开始
    public static final int CMD_GAME_OVER       = 17;   // 游戏结束（由客户端判断结束后发出，服务器只做转发）
    public static final int CMD_SEND_MESSAGE    = 18;   // 发送消息
    public static final int CMD_SET_NICKNAME    = 19;   // 设置昵称
    public static final int CMD_SET_AVANTAR     = 20;   // 设置头像
    public static final int CMD_SET_ROOM_NAME   = 21;   // 重命名房间
    public static final int CMD_GET_INFO        = 22;   //   // 获取某个实体信息（房间，对局，棋手）

    // send from server
    public static final int CMD_ROOM_NUM_CHANGED = 50;
    public static final int CMD_GAME_NUM_CHANGED = 51;
    public static final int CMD_ROOM_USER_CHANGED = 52;
    public static final int CMD_GAME_USER_CHANGED = 53;
    public static final int CMD_GAME_STATE_CHANGED = 54;
    public static final int CMD_USER_INFO_CHANGED = 55;
    public static final int CMD_HEART_BEAT      = 10000;
    public static final int CMD_OFFLINE         = 10001;    // 断线

    // 命令类型
    public static final int CMD_TYPE_SEND   = 0xff10;   // 发送请求
    public static final int CMD_TYPE_RESP   = 0xff11;   // 响应请求

    // 对弈双方操作（undo等），请求与回应
    // 对弈双方操作（undo等），请求与回应
    public static final int USER_REQUEST    = 1;
    public static final int USER_RESP       = 2;

    // 对方回应
    public static final int RESP_AGREE  = 1000;
    public static final int RESP_REJECT = 1001;

    // 错误码
    public static final int ERROR_NONE = 0;
    public static final int ERROR_TIMEOUT = -1;
    public static final int ERROR_NAME_EXIST = -2;
    public static final int ERROR_NOT_LOGIN = -3;
    public static final int ERROR_USER_NOT_IN_ROOM = -4;
    public static final int ERROR_USER_PLAYING_EXIT = -5;
    public static final int ERROR_INVALID_PASSWORD = -6;
    public static final int ERROR_ROOM_NOT_EXIST = -7;
    public static final int ERROR_UNDO_DISABLE = -8;

    public static final int ERROR_UNKNOWN = -100;

    /**
     * 每种棋对应一个服务端口
     * @param chessType
     * @return
     */
    public static int getPort(int chessType) {
        switch (chessType) {
            case ChessType.GO:
                return PORT_GO;
            case ChessType.CHINESE_CHESS:
                return PORT_CHINESE_CHESS;
            case ChessType.INTER_CHESS:
                return PORT_INTER_CHESS;
            case ChessType.GOBANG:
                return PORT_GOBANG;
            default:
                return PORT_GO;
        }
    }

    /////////////////////////////////////////////
    // client send各命令的GameObject中content成员数据
    /////////////////////////////////////////////
    protected static class SendDataBase implements Serializable {
        private static final long serialVersionUID = 2020L;
        public int userId;
        public SendDataBase(int userId) {
            this.userId = userId;
        }
    }

    protected static class LoginData implements Serializable {
        private static final long serialVersionUID = 2040L;
        public String name;
        public String password;
        public int chessType;
        public LoginData(String name, String password, int chessType) {
            this.name = name;
            this.password = password;
            this.chessType = chessType;
        }
    }

    protected static class LogoutData extends SendDataBase  {
        public LogoutData(int userId) {
            super(userId);
        }
    }

    protected static class CreateRoomData extends SendDataBase {
        public String roomName;
        public String password;    // 房间密码
        public CreateRoomData(int userId, String roomName, String password) {
            super(userId);
            this.roomName = roomName;
            this.password = password;
        }
    }

    protected static class GetRoomsData extends SendDataBase {
        public GetRoomsData(int userId) {
            super(userId);
        }
    }

    protected static abstract class RoomBase extends SendDataBase {
        public int roomId;
        public RoomBase(int userId, int roomId) {
            super(userId);
            this.roomId = roomId;
        }
    }

    protected static class EnterRoomData extends RoomBase {
        public String password;
        public EnterRoomData(int userId, int roomId, String password) {
            super(userId, roomId);
            this.password = password;
        }
    }

    protected static class ExitRoomData extends RoomBase {
        public ExitRoomData(int userId, int roomId) {
            super(userId, roomId);
        }
    }

    protected static class CreateGameData extends SendDataBase {
        public int roomId;
        public int num;
        public Object gameInfo;
        public CreateGameData(int userId, int roomId, int num, Object gameInfo) {
            super(userId);
            this.roomId = roomId;
            this.num = num;
            this.gameInfo = gameInfo;
        }
    }

    protected static abstract class GameBase extends SendDataBase {
        public int gameId;
        public GameBase(int userId, int gameId) {
            super(userId);
            this.gameId = gameId;
        }
    }

    protected static class JoinGameData extends GameBase {
        public JoinGameData(int userId, int gameId) {
            super(userId, gameId);
        }
    }

    protected static class QuitGameData extends GameBase {
        public QuitGameData(int userId, int gameId) {
            super(userId, gameId);
        }
    }

    protected static class PlayMoveData extends GameBase {
        public String move;
        public PlayMoveData(int userId, int gameId, String move) {
            super(userId, gameId);
            this.move = move;
        }
    }

    protected static class UndoData extends GameBase {
        public int code;       // USER_REQUEST, USER_RESP
        public int resp;       // RESP_AGREE, RESP_REJECT, valid only when code=USER_RESP
        public UndoData(int userId, int gameId, int code, int resp) {
            super(userId, gameId);
            this.code = code;
            this.resp = resp;
        }
    }

    protected static class ResignData extends GameBase {
        public ResignData(int userId, int gameId) {
            super(userId, gameId);
        }
    }

    protected static class DrawData extends UndoData {
        public DrawData(int userId, int gameId, int code, int resp) {
            super(userId, gameId, code, resp);
        }
    }

    protected static class GetMovesData extends GameBase {
        public GetMovesData(int userId, int gameId) {
            super(userId, gameId);
        }
    }

    protected static class ReadyData extends GameBase {
        public ReadyData(int userId, int gameId) {
            super(userId, gameId);
        }
    }

    protected static class GameStartData extends GameBase {
        public GameStartData(int userId, int gameId) {
            super(userId, gameId);
        }
    }

    protected static class GameOverData extends GameBase {
        public GameOverData(int userId, int gameId) {
            super(userId, gameId);
        }
    }

    protected static class SendMessageData extends SendDataBase {
        public int roomId;
        public int gameId;
        public int toUserId;
        public String msg;
        public SendMessageData(int userId, int roomId, int gameId, int toUserId, String msg) {
            super(userId);
            this.roomId = roomId;
            this.gameId = gameId;
            this.toUserId = toUserId;
            this.msg = msg;
        }
    }

    protected static class SetNickNameData extends SendDataBase {
        public String nickName;
        public SetNickNameData(int userId, String nickName) {
            super(userId);
            this.nickName = nickName;
        }
    }

    protected static class SetAvantarData extends SendDataBase {
        public int width;
        public int height;
        public int bits;     // 2--RGB565, 3--RGB888, 4--RGBA
        public String strPic;  // base64编码的图像数据
        public SetAvantarData(int userId, int width, int height, int bits, String strPic) {
            super(userId);
            this.width = width;
            this.height = height;
            this.bits = bits;
            this.strPic = strPic;
        }
    }

    protected static class SetRoomNameData extends SendDataBase {
        public String name;
        public int roomId;
        public SetRoomNameData(int userId, String name, int roomId) {
            super(userId);
            this.name = name;
            this.roomId = roomId;
        }
    }

    protected static class GetInfoData extends SendDataBase {
        public int id;
        public GetInfoData(int userId, int id) {
            super(userId);
            this.id = id;
        }
    }

    /////////////////////////////////////////////////////
    // server端发送响应数据
    /////////////////////////////////////////////////////
    protected static class RoomNumChangedData implements Serializable {
        private static final long serialVersionUID = 2041L;
        public List<IRoom> rooms;
        public RoomNumChangedData(List<IRoom> rooms) {
            this.rooms = new ArrayList<>();
            if(rooms != null)
                this.rooms.addAll(rooms);
        }
    }

    protected static class GameNumChangedData implements Serializable {
        private static final long serialVersionUID = 2042L;
        public IRoom room;
        public GameNumChangedData(IRoom room) {
            this.room = room;
        }
    }

    protected static class RoomUserChangedData implements Serializable {
        private static final long serialVersionUID = 2043L;
        public IRoom room;
        public IUser user;
        public boolean leave;
        public RoomUserChangedData(IRoom room, IUser user, boolean leave) {
            this.room = room;
            this.user = user;
            this.leave = leave;
        }
    }

    protected static class GameUserChangedData implements Serializable {
        private static final long serialVersionUID = 2044L;
        public IPlayGame game;
        public IUser user;
        public boolean leave;
        public GameUserChangedData(IPlayGame game, IUser user, boolean leave) {
            this.game = game;
            this.user = user;
            this.leave = leave;
        }
    }

    protected static class GameStateChangedData implements Serializable {
        private static final long serialVersionUID = 2045L;
        public IPlayGame game;
        public GameStateChangedData(IPlayGame game) {
            this.game = game;
        }
    }

    protected static class UserInfoChangedData implements Serializable {
        private static final long serialVersionUID = 2046L;
        public IUser user;
        public UserInfoChangedData(IUser user) {
            this.user = user;
        }
    }

    protected static class RespDataBase implements Serializable {
        private static final long serialVersionUID = 2047L;
        public IUser user;
        public int errorCode;
        public RespDataBase(IUser user) {
            this.user = user;
            errorCode = ERROR_NONE;
        }
    }

    protected static class RespCreateRoomData extends RespDataBase {
        public IRoom room;
        public RespCreateRoomData(IUser user, IRoom room) {
            super(user);
            this.room = room;
        }
    }

    protected static class RespGetRoomsData extends RespDataBase {
        public List<IRoom> rooms;
        public RespGetRoomsData(IUser user, List<IRoom> rooms) {
            super(user);
            this.rooms = new ArrayList<>();
            if(rooms != null)
                this.rooms.addAll(rooms);
        }
    }

    protected static class RespCreateGameData extends RespDataBase {
        public List<IPlayGame> games;
        public RespCreateGameData(IUser user, List<IPlayGame> games) {
            super(user);
            this.games = new ArrayList<>();
            if(games != null)
                this.games.addAll(games);
        }
    }

    protected static class RespGameData extends RespDataBase {
        public IPlayGame game;
        public RespGameData(IUser user, IPlayGame game) {
            super(user);
            this.game = game;
        }
    }

    protected static class RespPlayMoveData extends RespGameData {
        public String move;
        public RespPlayMoveData(IUser user, IPlayGame game, String move) {
            super(user, game);
            this.move = move;
        }
    }

    protected static class RespGetMovesData extends RespGameData {
        public List<String> moves;
        public RespGetMovesData(IUser user, IPlayGame game, List<String> moves) {
            super(user, game);
            this.moves = new ArrayList<>();
            if(moves != null)
                this.moves.addAll(moves);
        }
    }

    protected static class RespMessageData extends RespDataBase {
        public IRoom room;
        public IPlayGame game;
        public IUser toUser;
        public String msg;
        public RespMessageData(IUser user, IRoom room, IPlayGame game, IUser toUser, String msg) {
            super(user);
            this.room = room;
            this.game = game;
            this.toUser = toUser;
            this.msg = msg;
        }
    }

    protected static class RespGetInfoData extends RespDataBase {
        public int id;
        public Object obj;  // id对应的对象
        public RespGetInfoData(IUser user, int id, Object obj) {
            super(user);
            this.id = id;
            this.obj = obj;
        }
    }

    /////////////////////////////////////////////////////
    // socket数据收发及同步
    ////////////////////////////////////////////////////

    protected Object mSyncObj = new Object();
    protected GameObject mRecvData = null;

    @Override
    public void onReceive(Socket socket, Object data) {
        GameObject gameData = (GameObject) data;

        if (gameData.type == CMD_TYPE_RESP) {
            handleRespDate(gameData);
            // 响应数据直接返回
            mRecvData = gameData;
            synchronized (mSyncObj) {
                mSyncObj.notifyAll();
            }
            return;
        }

        handleReceiveData(socket, gameData);
    }

    protected abstract void handleReceiveData(Socket socket, GameObject data);
    protected void handleRespDate(GameObject data) {

    }
}
